using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.Experimental.GlobalIllumination;
using UnityEngine.UI;
using static DatasetLoader;
using static UnityEngine.ParticleSystem;

[RequireComponent(typeof(ParticleSystem))]
public class PointRenderer : MonoBehaviour
{

    [SerializeField] DatasetOptionUIController datasetOptionUIController;

    [SerializeField] float plotScale = 10;
    
    [SerializeField] float pointScale = 0.3f;

    [Header("Plot Label")]
    [SerializeField] TMP_Text datasetTitleLabel;
    [SerializeField] TMP_Text datasetInfomationLabel;
    [SerializeField] TMP_Text xMinLabel;
    [SerializeField] TMP_Text xMaxLabel;
    [SerializeField] TMP_Text yMinLabel;
    [SerializeField] TMP_Text yMaxLabel;
    [SerializeField] TMP_Text zMinLabel;
    [SerializeField] TMP_Text zMaxLabel;


    List<Vector3> m_pointList = new();
    Vector3 m_pmin, m_pmax;

    ParticleSystem m_particleSystem;

    // Start is called before the first frame update
    void Start()
    {
        m_particleSystem = gameObject.GetComponent<ParticleSystem>();
        var mainPar = m_particleSystem.main;
        mainPar.startLifetime = float.PositiveInfinity;
        m_particleSystem.Play();
        pointScale = datasetOptionUIController.pointSize;
    }

    // Update is called once per frame
    void Update()
    {
        pointScale = datasetOptionUIController.pointSize;
        UpdateParticles();
    }

    public void OnDataLoaded(string fileName, List<DataField> records)
    {
        if (records.Count == 0)
        {
            Debug.LogWarning(string.Format("No records in {0}", fileName));
            return;
        }

        Debug.Log(string.Format("There are {0} records in {1}", records.Count, fileName));

        m_pointList.Clear();

        m_pmax = m_pmin = new(records[0].x, records[0].y, records[0].z);

        foreach (var rec in records)
        {
            m_pmin.x = rec.x < m_pmin.x ? rec.x : m_pmin.x;
            m_pmin.y = rec.y < m_pmin.y ? rec.y : m_pmin.y;
            m_pmin.z = rec.z < m_pmin.z ? rec.z : m_pmin.z;

            m_pmax.x = rec.x > m_pmax.x ? rec.x : m_pmax.x;
            m_pmax.y = rec.y > m_pmax.y ? rec.y : m_pmax.y;
            m_pmax.z = rec.z > m_pmax.z ? rec.z : m_pmax.z;

            m_pointList.Add(new(rec.x, rec.y, rec.z));
        }

        AssignLabels(fileName, m_pointList.Count);

        CreateParticles();
    }

    public void OnDataLoadFailed(string failedInfo)
    {
        m_pointList.Clear();
        m_particleSystem.Clear();
        datasetTitleLabel.text = "Dataset Load Failed";
        datasetInfomationLabel.text = string.Format("Information: {0}", failedInfo);
        xMinLabel.text = "-";
        xMaxLabel.text = "-";
        yMinLabel.text = "-";
        yMaxLabel.text = "-";
        zMinLabel.text = "-";
        zMaxLabel.text = "-";
    }

    private void CreateParticles()
    {
        m_particleSystem.Clear();
        m_particleSystem.Emit(m_pointList.Count);
        var particlePoints = new ParticleSystem.Particle[m_pointList.Count];
        m_particleSystem.GetParticles(particlePoints);

        Vector3 diagonal = m_pmax - m_pmin;

        for (int i = 0; i < m_pointList.Count; i++)
        {
            float x = (m_pointList[i].x - m_pmin.x) / diagonal.x;
            float y = (m_pointList[i].y - m_pmin.y) / diagonal.y;
            float z = (m_pointList[i].z - m_pmin.z) / diagonal.z;

            particlePoints[i].position = new Vector3(x, y, z) * plotScale;
            particlePoints[i].startSize = pointScale;

            particlePoints[i].startColor = datasetOptionUIController.defaultPointColor;

        }

        m_particleSystem.SetParticles(particlePoints);
    }

    void UpdateParticles()
    {
        var particlePoints = new ParticleSystem.Particle[m_particleSystem.particleCount];
        m_particleSystem.GetParticles(particlePoints, m_particleSystem.particleCount);

        if (particlePoints.Length != m_pointList.Count)
        {
            //Debug.Log(string.Format("par: {0}, point: {1}", particlePoints.Length, m_pointList.Count));
            return;
        }

        Vector3 diagonal = m_pmax - m_pmin;

        for (int i = 0; i < m_pointList.Count; i++)
        {
            float x = (m_pointList[i].x - m_pmin.x) / diagonal.x;
            float y = (m_pointList[i].y - m_pmin.y) / diagonal.y;
            float z = (m_pointList[i].z - m_pmin.z) / diagonal.z;
            particlePoints[i].position = new Vector3(x, y, z) * plotScale;
            particlePoints[i].startSize = pointScale;
        }

        m_particleSystem.SetParticles(particlePoints, particlePoints.Length);
    }

    private void AssignLabels(string fileName, int size)
    {
        datasetTitleLabel.text = fileName;

        datasetInfomationLabel.text = string.Format("Data Size: {0}", size);

        xMinLabel.text = m_pmin.x.ToString("0.00");
        xMaxLabel.text = m_pmax.x.ToString("0.00");
                                               
        yMinLabel.text = m_pmin.y.ToString("0.00");
        yMaxLabel.text = m_pmax.y.ToString("0.00");
                                               
        zMinLabel.text = m_pmin.z.ToString("0.00");
        zMaxLabel.text = m_pmax.z.ToString("0.00");
    }

    private void OnParticleTrigger()
    {
        if (datasetOptionUIController.isNeither)
        {
            return;
        }
        List<Particle> enterParticles = new();

        var numEnter = m_particleSystem.GetTriggerParticles(ParticleSystemTriggerEventType.Enter, enterParticles);

        //Debug.Log(string.Format("Annotation or Erasing: {0}", numEnter));

        for (int i = 0; i < numEnter; i++)
        {
            var p = enterParticles[i];
            if (datasetOptionUIController.isAnnotating)
            {
                p.startColor = datasetOptionUIController.annotationPointColor;
            }
            else if (datasetOptionUIController.isErasing)
            {
                p.startColor = datasetOptionUIController.defaultPointColor;
            }
            enterParticles[i] = p;
        }
        m_particleSystem.SetTriggerParticles(ParticleSystemTriggerEventType.Enter, enterParticles);
    }
}
